﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Android.Util;
using FantasticMusicPlayer.dbo.Model;
using NAudio.Utils;
using NAudio.Wave;
using NightPlayer;
using NightPlayer.windows.utils;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Fx;
using Un4seen.Bass.AddOn.Tags;
using static Un4seen.Bass.Bass;

namespace FantasticMusicPlayer
{
    class BassPlayerImpl : IBassPlayer
    {
        private bool disposedValue;

        public bool hiresAudio = false;

        private float[] fft = new float[512];
        public float[] Spectrum {
            get {
                BASS_ChannelGetData(currentPlaying, fft, (int)BASSData.BASS_DATA_FFT1024);
                return fft;
            }
        }

        public event EventHandler<EventArgs> PlaybackChanged;

        public event EventHandler<EventArgs> RaiseStop;
        public event EventHandler<EventArgs> TimerTick;
        public long CurrentPosition
        {
            get
            {
                long result = (long)(BASS_ChannelBytes2Seconds(currentPlaying, BASS_ChannelGetPosition(currentPlaying, BASSMode.BASS_POS_BYTES)) * 1000L);

                //long result = BASS_ChannelGetPosition(currentPlaying, BASSMode.BASS_POS_INEXACT);
                if (result < 0) { result = 0; }
                return result;
            }
            set => BASS_ChannelSetPosition(currentPlaying, Math.Min((TotalPosition - 10) / 1000d, value / 1000d));
        }

        public long TotalPosition => Math.Max(1, (long)(BASS_ChannelBytes2Seconds(currentPlaying, BASS_ChannelGetLength(currentPlaying, BASSMode.BASS_POS_BYTES)) * 1000L) + 1);

        private float _volume = 1;
        public float Volume
        {
            get
            {
                return _volume;
            }
            set
            {
                _volume = value;
                updateVolume();
            }
        }
        public bool IsPlaying
        {
            get => BASS_ChannelIsActive(currentPlaying) == BASSActive.BASS_ACTIVE_PLAYING; set
            {
                if (value)
                {
                    Play();
                }
                else
                {
                    Pause();
                }
            }
        }
        /// <summary>
        /// 目标LUFS
        /// </summary>
        public float TargetLUFS = 1;
        private bool _bassboosted = false;
        public bool BassBoost
        {
            get => _bassboosted; set
            {
                _bassboosted = value;
                applyFxStatus();
            }
        }


        public string album = "Loading...", title = "Night Player", artist = "ZYFDroid";
        public Android.Graphics.Bitmap cover;

        public event EventHandler<EventArgs> Stopped;
        public event EventHandler<AlbumEventArgs> CoverAvailable;
        public event EventHandler<SongInfoEventArgs> SongInfoAvailable;

        BASSTimer updateTimer;

        const BASSInit BASS_DEVICE_AUDIOTRACK = (BASSInit)0x20000;


        public void init()
        {
            BASS_SetConfig(BASSConfig.BASS_CONFIG_FLOATDSP, true);
            // 以防你不知道，要想在BASS中使用AUDIOTRACK，你需要使用Java的方式载入Bass库
            // 不然就会炸
            // 但这里是C#，要怎么办呢？答案如下↓
            Java.Lang.JavaSystem.LoadLibrary("bass");
            // 以及使用了AudioTrack之后就可以在某些设备上开启HiFi功能了
            var config = BASS_DEVICE_AUDIOTRACK;
            // config = BASSInit.BASS_DEVICE_DEFAULT;

            if (BASS_Init(-1, 48000, config, IntPtr.Zero))
            {
                postInit();
                return;
            }
            if (BASS_Init(-1, 44100, config, IntPtr.Zero))
            {
                postInit();
                return;
            }
            BASSError berr = BASS_ErrorGetCode();
            throw new Exception("BASS INIT ERROR: " + berr);
        }

        private void postInit() {

            Console.WriteLine(Un4seen.Bass.AddOn.Aac.BassAac.SupportedStreamExtensions);
            Console.WriteLine(Un4seen.Bass.AddOn.Ape.BassApe.SupportedStreamExtensions);
            Console.WriteLine(BassFx.BASS_FX_GetVersion());
            Console.WriteLine(Un4seen.Bass.AddOn.Flac.BassFlac.SupportedStreamExtensions);
            Console.WriteLine(Un4seen.Bass.AddOn.Opus.BassOpus.SupportedStreamExtensions);
            Console.WriteLine(Un4seen.Bass.AddOn.Wv.BassWv.SupportedStreamExtensions);
            Console.WriteLine(Bass.BASS_GetDevice());
            updateTimer = new BASSTimer(333);
            updateTimer.Tick += UpdateTimer_Tick;
        }


        public bool ScheduledStop { get; set; } = false;
        public long RemainMinute
        {
            get
            {
                return (StopTime - Java.Lang.JavaSystem.CurrentTimeMillis() + 59000L) / 60000L;
            }
            set
            {
                StopTime = Java.Lang.JavaSystem.CurrentTimeMillis() + value * 60000;
            }
        }
        public long RemainSecond
        {
            get
            {
                return (StopTime - Java.Lang.JavaSystem.CurrentTimeMillis()) / 1000L;
            }
        }

        int channelNoise = 0;
        int channelBbt = 0;

        private int channelNoiseType = 0;
        private int channelBbtType = 0;
        private float channelNoiseVolume = 0.5f;
        private float channelBbtVolume = 0.5f;

        private bool _looping = false;
        public bool Looping
        {
            get => _looping;
            set
            {
                this._looping = value;
                BASSFlag loopFlag = this._looping ? BASSFlag.BASS_SAMPLE_LOOP : ~BASSFlag.BASS_SAMPLE_LOOP;
                BASSFlag result = Bass.BASS_ChannelFlags(currentPlaying, loopFlag, BASSFlag.BASS_SAMPLE_LOOP);
            }
        }

        public String basePath = "";

        void loadToChannel(ref int which, float volume, String path) {
            Bass.BASS_ChannelStop(which);
            Bass.BASS_StreamFree(which);
            if (string.IsNullOrWhiteSpace(path)) {
                which = 0;
                return;
            }
            nint user = AndroidIO.BassStreamHelper.NextBassStreamUserPtr;
            which = Bass.BASS_StreamCreateFileUser(BASSStreamSystem.STREAMFILE_NOBUFFER, BASSFlag.BASS_DEFAULT,AndroidIO.File.OpenBass(path, user), user);
            Bass.BASS_ChannelSetAttribute(which, BASSAttribute.BASS_ATTRIB_VOL, volume);
            Bass.BASS_ChannelFlags(which, BASSFlag.BASS_SAMPLE_LOOP, BASSFlag.BASS_SAMPLE_LOOP);
            if(IsPlaying)
                Bass.BASS_ChannelPlay(which, true);
        }






        public void SetNoiseChannelNoise(String path)
        {
            loadToChannel(ref channelNoise,channelNoiseVolume,path);
        }
        public void SetBbtChannelNoise(String path)
        {
            loadToChannel(ref channelBbt, channelBbtVolume, path);
        }


        void setChannelVolume(int channel, ref float volume, float newVol) {
            volume = newVol;
            Bass.BASS_ChannelSetAttribute(channel, BASSAttribute.BASS_ATTRIB_VOL, newVol);
        }

        void setChannelType(ref int channel, ref int type, String[] paths, int newtype,float volume) {
            loadToChannel(ref channel, volume, paths[newtype % paths.Length]);
            type = newtype % paths.Length;
        }

        public float VolumeChannelNoise { get => channelNoiseVolume;  set => setChannelVolume(channelNoise,ref channelNoiseVolume,value); }
        public float VolumeChannelBbt { get => channelBbtVolume; set => setChannelVolume(channelBbt, ref channelBbtVolume, value); }
        public int ChannelBbtType { get => channelBbtType; set => channelBbtType = value; }
        public int ChannelNoiseType { get => channelNoiseType;
            set => channelNoiseType = value;
        }

        long StopTime = -1;

        long StopBeginTime = -1;

        private void UpdateTimer_Tick(object sender, EventArgs e)
        {
            if (IsPlaying)
            {
                if (ScheduledStop)
                {
                    if (StopTime < Java.Lang.JavaSystem.CurrentTimeMillis())
                    {
                        Android.Util.Log.Error("自动停止音乐播放：因为", "睡眠计时器触发");
                        RaiseStop?.Invoke(this, EventArgs.Empty);
                        Stop();
                        ScheduledStop = false;
                    }

                    if (StopTime - Java.Lang.JavaSystem.CurrentTimeMillis() < 15000) {
                        Volume *= 0.92f;
                        VolumeChannelBbt *= 0.92f;
                        VolumeChannelNoise *= 0.92f;
                    }
                }
                if (StopBeginTime != -1) { StopBeginTime = -1; }
                TimerTick?.Invoke(this, EventArgs.Empty);
            }
            else
            {
                if (!loading && BASS_ChannelIsActive(currentPlaying) == BASSActive.BASS_ACTIVE_STOPPED && CurrentPosition > TotalPosition / 2)
                {
                    updateTimer.Stop();
                    Stopped?.Invoke(this, EventArgs.Empty);
                }
                if (StopBeginTime == -1)
                {
                    StopBeginTime = Java.Lang.JavaSystem.CurrentTimeMillis();
                }
                if (Java.Lang.JavaSystem.CurrentTimeMillis() - StopBeginTime > 600000)
                {
                    AquireRaiseStop();
                    StopBeginTime = Java.Lang.JavaSystem.CurrentTimeMillis();
                }

            }
        }



        int currentPlaying = 0;
        object loadingSync = new object();
        SongEntry lastname;
        bool loading = false;
        public void Load(SongEntry entry)
        {
            string filename = entry.Path;
            lastname = entry;
            loading = true;
            updateTimer.Stop();
            BASS_StreamFree(currentPlaying);

           

            currentPlaying = createStreamByFormat(filename);


            if (currentPlaying == 0)
            {
                Android.Util.Log.Error("BassPlayer", "Can not play music: " + Bass.BASS_ErrorGetCode());
                Stopped?.Invoke(this, EventArgs.Empty);
                return;
            };

            initFx();
            applyFxStatus();
            //if (_drcompress)
            //{
            //    float postgain = 1;
            //    Stopwatch stopwatch = Stopwatch.StartNew();
            //    if (Un4seen.Bass.Utils.GetNormalizationGain(filename, 10, -1, -1, ref postgain) < 0) {
            //        postgain = 1;
            //    };
            //    stopwatch.Stop();
            //    Console.WriteLine("Scanning peak take "+stopwatch.ElapsedMilliseconds+" ms");
            //    Console.WriteLine("Gain = "+Decibels.LinearToDecibels(postgain)+" dB");

            //    postProcessGain = BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_VOLUME, 0);
            //    BASS_BFX_VOLUME volumeParam = new BASS_BFX_VOLUME();
            //    volumeParam.fVolume = postgain;
            //    BASS_FXSetParameters(postProcessGain, volumeParam);
            //}

            BASS_CHANNELINFO info = new BASS_CHANNELINFO();
            BASS_ChannelGetInfo(currentPlaying, info);
            int samplerate = info.freq;
            int channels = info.chans;

            SurroundSound.init(samplerate, channels);
            BASS_ChannelSetDSP(currentPlaying, SurroundSound.DspDelegate, IntPtr.Zero, 0);
            loading = false;
            updateTimer.Start();
            bool b = Looping;
            Looping = b.ToString().ToLower().StartsWith("t");

            TAG_INFO tag = new TAG_INFO(filename);
            nint tagBinary = Bass.BASS_ChannelGetTags(currentPlaying, BASSTag.BASS_TAG_ID3V2);
            if(tagBinary != 0)
            {
                BassTags.ReadID3v2(tagBinary, tag);
            }
            else
            {
                tagBinary = Bass.BASS_ChannelGetTags(currentPlaying, BASSTag.BASS_TAG_ID3);
                if (tagBinary != 0)
                {
                    BassTags.ReadID3v1(tagBinary, tag);
                }
            }
            
            if (tag.title == null || tag.title == "") { tag.title = AndroidIO.Path.GetFileName(filename); tag.artist = "Various Artist"; tag.album = "Unknown"; }

            bool isHiResAudio = filename.ToLower().EndsWith(".flac") || filename.ToLower().EndsWith(".ape") || filename.ToLower().EndsWith(".wav");

            SongInfoAvailable?.Invoke(this, new SongInfoEventArgs(tag.title, tag.artist, tag.album) { HiResAudio = isHiResAudio });

            if (tag.PictureCount > 0)
            {
                try
                {
                    TagPicture pic = tag.PictureGet(0);
                    Android.Graphics.Bitmap bmp = null;
                    if (pic.PictureStorage == TagPicture.PICTURE_STORAGE.Internal)
                    {
                        bmp = Android.Graphics.BitmapFactory.DecodeByteArray(pic.Data, 0, pic.Data.Length);
                    }
                    CoverAvailable?.Invoke(this, new AlbumEventArgs(bmp, false));
                }
                catch
                {

                    CoverAvailable?.Invoke(this, new AlbumEventArgs(null, true));
                }
            }
            else
            {
                CoverAvailable?.Invoke(this, new AlbumEventArgs(null, true));
            }
        }





        public DSPClass SurroundSound = new DspSwitcher();


        private int createStreamByFormat(String filename) {
            String extension = AndroidIO.Path.GetExtension(filename).ToLower().Replace(".","");
            nint user = AndroidIO.BassStreamHelper.NextBassStreamUserPtr;
            var bassStream = AndroidIO.File.OpenBass(filename, user);
            switch (extension) {
                case "flac":
                    return Un4seen.Bass.AddOn.Flac.BassFlac.BASS_FLAC_StreamCreateFileUser(BASSStreamSystem.STREAMFILE_NOBUFFER,BASSFlag.BASS_DEFAULT,bassStream, user);
                case "aac":
                    return Un4seen.Bass.AddOn.Aac.BassAac.BASS_AAC_StreamCreateFileUser(BASSStreamSystem.STREAMFILE_NOBUFFER, BASSFlag.BASS_DEFAULT, bassStream, user);
                case "ape":
                    return Un4seen.Bass.AddOn.Ape.BassApe.BASS_APE_StreamCreateFileUser(BASSStreamSystem.STREAMFILE_NOBUFFER, BASSFlag.BASS_DEFAULT, bassStream, user);
                default:
                    return Un4seen.Bass.Bass.BASS_StreamCreateFileUser(BASSStreamSystem.STREAMFILE_NOBUFFER, BASSFlag.BASS_DEFAULT, bassStream, user);
            }
        }

        int fxcompressor = 0;
        int fxgainparam = 0;
        int fxcompressorF1 = 0;
        int fxcompressorF2 = 0;
        private int fxvolume = 0;
        private List<int> appliedFx = new List<int>();
        private List<FxObject> fxobjects = new List<FxObject>();
        private float baseGAIN = 1;

        void initFx()
        {
            fxgainparam = Bass.BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_DAMP, 1); 
            fxvolume = Bass.BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_DAMP, 200);
            BASS_BFX_DAMP param3 = new BASS_BFX_DAMP();
            param3.fGain = baseGAIN;
            Bass.BASS_FXSetParameters(fxgainparam, param3);
            param3.fGain = this._volume;
            Bass.BASS_FXSetParameters(fxvolume, param3);

            setCompressorState(_drcompress);

            
        }
        private void setCompressorState(int state)
        {

            BASS_ChannelRemoveFX(currentPlaying, fxcompressorF1);
            BASS_ChannelRemoveFX(currentPlaying, fxcompressorF2);
            BASS_ChannelRemoveFX(currentPlaying, fxcompressor);
            if (state == 1)
            {
                if (lastname.Loudness != null && this.TargetLUFS < 0)
                {

                    var recommandedLUFS = -20f;
                    var toRecommandLUFSDelta = 0f;
                    if (TargetLUFS > recommandedLUFS)
                    {
                        toRecommandLUFSDelta = TargetLUFS - recommandedLUFS;
                    }

                    var dbGain = TargetLUFS - lastname.Loudness.LUFS;
                    var dbGainMul = 1.0f;
                    if (dbGain < 0)
                    {
                        dbGainMul = 0.7f;
                    }
                    else
                    {
                        dbGainMul = 0.7f;
                    }

                    Console.WriteLine($"Song LUFS: {lastname.Loudness.LUFS:0.0}, TargetLUFS: {TargetLUFS:0.0} - {toRecommandLUFSDelta:0.0} = {TargetLUFS - toRecommandLUFSDelta:0.0}, DBGain: {dbGain:0.0} * {dbGainMul} - {toRecommandLUFSDelta:0.0} = {dbGain * dbGainMul - toRecommandLUFSDelta:0.0} dB");
                    dbGain *= dbGainMul;
                    dbGain -= toRecommandLUFSDelta;



                    BASS_BFX_DAMP param3 = new BASS_BFX_DAMP();
                    param3.fGain = (float)Decibels.DecibelsToLinear(dbGain);
                    fxcompressor = Bass.BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_DAMP, 2);
                    Bass.BASS_FXSetParameters(fxcompressor, param3);
                }
            }
            else if (state == 2)
            {
                var compressor = new BASS_BFX_COMPRESSOR2();
                fxcompressor = Bass.BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_COMPRESSOR2, 10);
                float bassBypass = -9.5f;
                compressor.Preset_Hard2();
                compressor.fAttack = 4;
                compressor.fRelease = 2000;
                compressor.fRatio = 4.7f;
                compressor.fThreshold = -27f;
                compressor.fGain = (compressor.fThreshold) / compressor.fRatio - compressor.fThreshold;
                compressor.fGain -= -bassBypass;
                compressor.fGain -= 0.7320508f;

                BASS_BFX_BQF bfxparam = new BASS_BFX_BQF();
                bfxparam.fBandwidth = 0;
                bfxparam.fQ = 1f;
                bfxparam.fS = 0f;
                bfxparam.fCenter = 200;
                bfxparam.fGain = bassBypass;
                bfxparam.lFilter = BASSBFXBQF.BASS_BFX_BQF_LOWSHELF;
                fxcompressorF1 = BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_PEAKEQ, 11);
                BASS_FXSetParameters(fxcompressorF1, bfxparam);
                bfxparam.fGain = -bfxparam.fGain;
                fxcompressorF2 = BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_PEAKEQ, 9);
                BASS_FXSetParameters(fxcompressorF2, bfxparam);
                Bass.BASS_FXSetParameters(fxcompressor, compressor);

            }

        }
        private int _drcompress = 0;
        public int DynamicRangeCompressed
        {
            get { return _drcompress; }
            set
            {
                this._drcompress = value;
                setCompressorState(value);
            }
        }
        private void AppendFx(float freq, float octivate, float gain)
        {
            BASS_BFX_PEAKEQ param = new BASS_BFX_PEAKEQ();
            param.fBandwidth = 0;
            param.fQ = 1 / octivate;
            param.fCenter = freq;
            param.fGain = gain;
            int fxhandle = BASS_ChannelSetFX(currentPlaying, BASSFXType.BASS_FX_BFX_PEAKEQ, 0);
            BASS_FXSetParameters(fxhandle, param);
            appliedFx.Add(fxhandle);
        }

        void updateVolume()
        {
            BASS_BFX_DAMP param3 = new BASS_BFX_DAMP();
            param3.fGain = this._volume;
            Bass.BASS_FXSetParameters(fxvolume, param3);
        }
        private void ClearFx()
        {
            appliedFx.ForEach(fx => BASS_ChannelRemoveFX(currentPlaying, fx));
        }

        public void LoadFx(string fxfile)
        {
            if (fxfile != null)
            {
                if (fxfile.ToLower().EndsWith(".eq"))
                {
                    ApplyLegacyFx(fxfile);
                    return;
                }
                if (fxfile.ToLower().EndsWith(".wav"))
                {
                    ApplyLegacyFx(null);
                    ApplyConvolverFx(fxfile);
                    return;
                }
            }

           
            ApplyLegacyFx(null);

        }


        void ApplyConvolverFx(string fxfile)
        {
            using (var convReader = new NAudio.Wave.WaveFileReader(AndroidIO.File.OpenRead(fxfile)))
            {
                WaveFormat format = convReader.WaveFormat;
                if(format.Channels != 4)
                {
                    throw new System.IO.InvalidDataException("无效的脉冲响应文件");
                }
                if(convReader.TotalTime > TimeSpan.FromMilliseconds(10000))
                {
                    throw new System.IO.InvalidDataException("文件过大");
                }
            }
            DspSwitcher switcher = this.SurroundSound as DspSwitcher ;
            if(switcher != null)
            {
                switcher.WrappedDSP = new SpeakerInRoomDSP(fxfile);
                switcher.Enabled = true;
            }
        }

        void ApplyLegacyFx(string fxfile)
        {
            DspSwitcher dspSwitcher = this.SurroundSound as DspSwitcher;
            if (dspSwitcher.WrappedDSP != null && dspSwitcher.WrappedDSP is SpeakerInRoomDSP)
            {
                SpeakerInRoomDSP dsp = dspSwitcher.WrappedDSP as SpeakerInRoomDSP;
                if (dsp.firSource != null)
                {
                    dspSwitcher.WrappedDSP = null;
                }
            }

            if (fxfile == null || fxfile == "" || !AndroidIO.File.Exists(fxfile))
            {
                fxobjects.Clear();
                baseGAIN = 1;
                applyFxStatus();
                return;
            }

            //String[] lines = File.ReadAllLines(fxfile);
            String[] lines = AndroidIO.File.ReadAllLines(fxfile);
            List<FxObject> fxObjects = new List<FxObject>();
            float gain = 0;
            try
            {
                gain = float.Parse(lines[0]);
                for (int i = 1; i < lines.Length; i++)
                {
                    string line = lines[i];
                    if (line.Trim() != "")
                    {
                        string[] param = line.Split(',');
                        float p1 = float.Parse(param[0].Trim());
                        float p2 = float.Parse(param[1].Trim());
                        float p3 = float.Parse(param[2].Trim());
                        fxObjects.Add(new FxObject() { Frequent = p1, BandwidthOctivates = p2, Gain = p3 });
                    }
                }
                fxobjects.Clear();
                fxobjects.AddRange(fxObjects);
                baseGAIN = gain;
                applyFxStatus();
            }
            catch (Exception ex)
            {
                Log.Warn("LoadFX", ex.ToString());
                throw ex;
            }
        }

        void applyFxStatus()
        {
            BASS_BFX_DAMP param3 = new BASS_BFX_DAMP();
            Bass.BASS_FXGetParameters(fxgainparam, param3);
            param3.fGain = baseGAIN;
            Bass.BASS_FXSetParameters(fxgainparam, param3);
            ClearFx();
            fxobjects.ForEach(fo => AppendFx(fo.Frequent, fo.BandwidthOctivates, fo.Gain));
        }
        public void Pause()
        {
            BASS_ChannelPause(currentPlaying);
            if (channelBbt != 0) {
                BASS_ChannelPause(channelBbt);
            }
            if (channelNoise != 0) {
                BASS_ChannelPause(channelNoise);
            }
            PlaybackChanged?.Invoke(this,null);
        }

        public void Play()
        {
            updateTimer.Enabled = true;
            if(!BASS_ChannelPlay(currentPlaying, false))
            {
                Console.WriteLine("Start Playing: " +BASS_ErrorGetCode());
            }
            PlaybackChanged?.Invoke(this,null); 
            if (channelBbt != 0)
            {
                BASS_ChannelPlay(channelBbt,false);
            }
            if (channelNoise != 0)
            {
                BASS_ChannelPlay(channelNoise,false);
            }
        }

        public void PlayOrPause()
        {
            IsPlaying = !IsPlaying;
        }

        public void Replay()
        {
            BASS_ChannelPlay(currentPlaying, true);
        }

        public void Stop()
        {
            BASS_ChannelPause(currentPlaying);
            BASS_ChannelSetPosition(currentPlaying, 0);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    cover?.Dispose();
                  
                    Stop();
                    updateTimer.Stop();
                    updateTimer.Dispose();
                }
                BASS_ChannelStop(channelBbt);
                BASS_ChannelStop(channelNoise);
                BASS_SampleFree(currentPlaying);
                BASS_SampleFree(channelBbt);
                BASS_SampleFree(channelNoise);
                BASS_Free();
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        public void AquireRaiseStop()
        {
            RaiseStop?.Invoke(this, EventArgs.Empty);
        }
    }

    public class FxObject
    {
        public float Frequent { get; set; }
        public float BandwidthOctivates { get; set; }
        public float Gain { get; set; }
    }

}
